import TJavaLib.TList;

public class TSimulator implements Runnable {
	private long simTime, timeTick, duraTime, refreshRate;
	private boolean stop, pause, started;
	private TList jobQueue, execQueue;
	private TNetwork network;
	
	public TSimulator() {
		started = false;
		stop = true;
		pause = false;
		timeTick = 1;
		refreshRate = 0;
		jobQueue = new TList();
		execQueue = new TList();
	}
	
	public TNetwork newNetwork() {
		network = new TNetwork(this);
		return network;
	}
	
	public TNetwork getNetwork() {
		return network;
	}
	
	public long getSimTime() { 
		return simTime; 
	}
	
	public void setDuraTime(long dura) {
		duraTime = dura;
	}
	
	public long getRefreshRate() {
		return refreshRate;
	}
	
	public void setRefreshRate(long rate) {
		refreshRate = rate;
	}
	
	public long getTimeTick() {
		return timeTick;
	}
	
	public void setTimeTick(long tick) {
		timeTick = tick;
	}
	
	public boolean isStarted() {
		return started;
	}
	
	public boolean isStoped() {
		return stop;
	}
	
	public void setStarted(boolean flag) {
		started = flag;
	}
	
	//-------------------Job Handler-------------------------------------------------
	public void addJob(TJob job) {
		jobQueue.enQueue(job);
	}
	
	public void addJobAtTime(TJob job, String atTime) {
		job.setExecTimeS(atTime);
		jobQueue.enQueue(job);
	}
	
	public void addJobInTime(TJob job, long inTime) {
		job.setExecTime(simTime+inTime);
		jobQueue.enQueue(job);
	}
	
	public void delJobOf(TObject jobCreator) {
		TList tempQueue = new TList();
		while (!jobQueue.isEmpty()) {
			TJob job = (TJob)jobQueue.deQueue();
			if (!jobCreator.equals(job.getCreator()))
				tempQueue.enQueue(job);
		}
		while (!tempQueue.isEmpty())
			jobQueue.enQueue(tempQueue.deQueue());
	}
	
	//-----------------Thread related------------------------------------------------
	public void start() { 
		if (network != null) {
			stop = false;
			TList net = network.getNetwork();
			if (net.len() > 0) {
				simTime = 0;
				//Start the simulation
				new Thread(this).start();
			}
		}
	}
	
	public void stop() {
		if (!stop) {
			stop = true;
			jobQueue.clear();
		}
	}
	
	public synchronized void pause() { 
		pause = true; 
	}
	
	public synchronized void resume() { 
		pause = false; 
		notify(); 
	}
   
  @Override
	public void run() {
		System.out.println("Simulation started at time: "+simTime);
		long startTime = System.currentTimeMillis();
	
		//Call initial methods
		TList net = network.getNetwork();
		for (int i=0; i<net.len(); i++) {
			TNode node = (TNode)net.get(i);
			node.invokeBootup();
		}
		
		//Simulation started
		while (!stop) {
			try {
				//Clear the job queue
				//TList execQueue = new TList();
				while (!jobQueue.isEmpty())
					execQueue.enQueue(jobQueue.deQueue());
					
				//Process the registered jobs
				while (!execQueue.isEmpty() && !stop) {
					TJob job = (TJob)execQueue.deQueue();
					
					boolean postpone = false;
					long execTime = job.getExecTime();
					if (execTime > 0) {
						if (Math.abs(simTime-execTime) > timeTick/2) {
							jobQueue.enQueue(job);
							postpone = true;
						}
					}
					
					//Process the job here
					if (!postpone) {
						TObject jobCreator = job.getCreator();
						switch (job.getJobType()) {
							case TJob.SEND:
								TList nList = network.get1HopNeighbors((TNode)jobCreator);
								for (int i=0; i<nList.len(); i++) {
									TNode node = (TNode)nList.get(i);
									TJob newJob = new TJob(TJob.RECEIVE, TTask.NONE, job.getPacket(), null, node);
									addJob(newJob);
								} 
								break;
							
							case TJob.RECEIVE:
								TNode jobDoer = (TNode)job.getDoer();
								jobDoer.execute(job);
								break;
								
							case TJob.TASK:
								jobCreator.invokeCallback(job.getTaskName());
								break;
								
							default: break;
						}
					}
				}
				
				//Increase simulation time with 1ms
				simTime += timeTick;
				
				if (simTime >= duraTime) stop = true;
				
				//Thread.currentThread();
				//Sleep 10ms to relax the CPU
				Thread.sleep(refreshRate);
				synchronized (this) { while (pause) wait(); }
			} catch (InterruptedException e){}
		}
		execQueue.clear();
		System.out.println("Simulation ended at time: "+simTime);
		System.out.println("Simulation duration: "+duraTime+"ms");
		long stopTime = System.currentTimeMillis();
		System.out.println("Realtime duration: "+(stopTime-startTime)+"ms");
	}
}
